package 图论述

/*
https://leetcode.cn/problems/course-schedule/?envType=study-plan-v2&envId=top-100-liked

207. 课程表
你这个学期必须选修 numCourses 门课程，记为 0 到 numCourses - 1 。
在选修某些课程之前需要一些先修课程。 先修课程按数组 prerequisites 给出，
其中 prerequisites[i] = [ai, bi] ，表示如果要学习课程 ai 则 必须 先学习课程  bi 。

例如，先修课程对 [0, 1] 表示：想要学习课程 0 ，你需要先完成课程 1 。
请你判断是否可能完成所有课程的学习？如果可以，返回 true ；否则，返回 false 。

示例 1：
输入：numCourses = 2, prerequisites = [[1,0]]
输出：true
解释：总共有 2 门课程。学习课程 1 之前，你需要完成课程 0 。这是可能的。

示例 2：
输入：numCourses = 2, prerequisites = [[1,0],[0,1]]
输出：false
解释：总共有 2 门课程。学习课程 1 之前，你需要先完成课程 0 ；
并且学习课程 0 之前，你还应先完成课程 1 。这是不可能的。
*/
func canFinish(numCourses int, prerequisites [][]int) bool {

	edges := make([][]int, numCourses) //向学i课程，必须先学完i对应的数组中的课程
	preCount := make([]int, numCourses)
	var result []int

	for _, info := range prerequisites { //遍历先修数组
		edges[info[1]] = append(edges[info[1]], info[0])
		preCount[info[0]]++ //需要先修课程计数
	}

	var q []int                       //创建队列
	for i := 0; i < numCourses; i++ { //遍历课程种类
		if preCount[i] == 0 { //如果当前课程需要前置学习的课程为0，证明可以直接学习了,直接放入队列
			q = append(q, i)
		}
	}

	for len(q) > 0 { //只要队列长度>0,持续循环
		curClass := q[0] //获取队列头部元素
		q = q[1:]        //元素出队

		result = append(result, curClass)   //将当前课程加入结果队列
		for _, v := range edges[curClass] { //想学u课程,必须学完u对应的数组
			preCount[v]--
			if preCount[v] == 0 {
				q = append(q, v)
			}
		}
	}
	return len(result) == numCourses
}
